Built with R version 4.4.0.

1 Intro

Here, I document the steps taken in wrangling the NOAA Nearshore Fish Atlas (NFA). This replaces the separate scripts (named “./FishAtlas_#_description.R”) and combines them into a single, streamlined R markdown file. These R scripts will be archived in case I need to replicate older methods. Namely, I used to use rgdal package for some steps in the wrangling process but that package stopped being supported ca. Oct 2023 in lieu of the sf package.

Link to the NFA can be found here, https://alaskafisheries.noaa.gov/mapping/sz/. The entire database can be downloaded as a .csv file, which is what I use here. However, I have additional data files that were either shared with me by NFA contect managers or modified from the NOAA NFA data directory for ease of uploading into R. I try to document these cases in the future.

2 Wrangle site data

The idea with this initial step is to define the scale of our samples. Contributors to the NFA did not define their SiteID’s and EventID’s similar to each other, so we’ll need to distinguish between SiteID vs EventID and make sure it’s consistent throughout the data.

I define a SiteID as a unique place in space, i.e., all samples from the same ‘beach’ (will be defined later on). An EventID is a unique pull of the beach seine. So there can be single or multiple EventID’s associated with a SiteID. Additionally, I introduce a VisitID, which adds a temporal distinction among SiteID’s, i.e., all events from the same beach on the same day.

2.1 Set up

Load required packages, define directory, set options, source scripts:

# Packages
library(tidyverse)
library(lubridate)
library(here)
library(skimr)
library(sf)

# Directory
wd = here()
dirs = wd %>% list.files() %>% str_subset(pattern = "^README|^LICENSE|.md$|.Rproj$", negate = TRUE)
for (i in seq_along(dirs)) {
  name = str_replace_all(dirs[i], "^", "dir.")
  path = str_replace_all(dirs[i], "^", str_c(wd, "/"))
  assign(name, path)
  rm(name, path, i)
}

# Options

# Source

2.2 Read in data

data = read_csv(file.path(dir.data, "FishAtlas_BeachSeines_2022.07.12.csv"),
               show_col_types = FALSE)
GearLookup = read_csv(file.path(dir.data, "FishAtlasExpansion_040422_Lookup_Gear.csv"),
                      show_col_types = FALSE)

2.3 Separate site and species info

As in typical community data (site x species), we have info tied to site description and info tied to taxa. Notice that EventID is the ‘look-up’ key used in both data sets.

events.1 = data %>%
  select(SiteID, EventID,
         Date,
         Lat, Lon = Long, Region, Location,
         Habitat, TidalStage, Temp_C, Salinity,
         GearSpecific, ProjectName, PointOfContact) %>%
  mutate(Date = mdy(Date)) %>% # re-format date
  distinct()

catch.1 = data %>%
  select(EventID,
         Sp_CommonName, Sp_ScientificName, Fam_CommonName, Fam_ScientificName,
         Unmeasured, Length_mm, LengthType, LifeStage)

2.4 Considering gear type

The NFA website makes it easy to filter for gear type, so I done prior to downloading the .csv file. However, we’ll want to account for differences among the beach seines used- namely the net mesh size. This information is contained in the metadata for the NFA database and can be accessed by request from the database managers (file FishAtlasExpansion_040422_Lookup_Gear.csv). A few instances of missing mesh size information was provided by email request to the associated point of contact for that contributed data. Results from the below code are hidden but comments explain what decisions were made.

# Join the mesh size info from our gear data to the site information
gear.1 = left_join(events.1, select(GearLookup, GearSpecific, MeshSize), by = "GearSpecific")

# Subset for gear related info and take a look
gear.2 = select(gear.1, EventID, MeshSize, GearSpecific, ProjectName, PointOfContact)
glimpse(gear.2)
# How many different mesh sizes are there?
gear.2$MeshSize %>% unique()

# Looks like we have an NA to take care of...
filter(gear.2, is.na(MeshSize))
# These are all from the GOAIERP project (PI Olav Ormseth). The report from that project reports 6 mm stretched mesh.
# We'll just go ahead and fix it here instead of changing the GearLookup file,
gear.3 = mutate(gear.2, MeshSize = ifelse(is.na(MeshSize), 6, MeshSize))

# Check our mesh sizes again,
gear.3$MeshSize %>% unique()
# Great, no more NA's.

# We can now join this information with our site data,
events.2 = left_join(events.1, select(gear.3, EventID, MeshSize), by = "EventID")

2.5 The “Site-Event” problem

The catch data is tied to unique EventIDs which are associated with spatially explicit SiteIDs (each SiteID has a unique lat/lon). Considering that the data has been contributed by multiple researchers/projects, we’ll need to make sure we have consistent sites and events across the whole dataset. After that we can tackle the catch data. First, we take a look at all of the site data with skimr::skim().

skim(events.2)
Data summary
Name events.2
Number of rows 4473
Number of columns 15
_______________________
Column type frequency:
character 7
Date 1
numeric 7
________________________
Group variables None

Variable type: character

skim_variable n_missing complete_rate min max empty n_unique whitespace
Region 0 1.00 10 16 0 5 0
Location 1 1.00 8 135 0 406 0
Habitat 0 1.00 4 13 0 8 0
TidalStage 1007 0.77 3 5 0 6 0
GearSpecific 0 1.00 8 12 0 15 0
ProjectName 0 1.00 4 31 0 31 0
PointOfContact 0 1.00 91 151 0 11 0

Variable type: Date

skim_variable n_missing complete_rate min max median n_unique
Date 0 1 1976-05-29 2021-09-09 2006-04-25 1035

Variable type: numeric

skim_variable n_missing complete_rate mean sd p0 p25 p50 p75 p100 hist
SiteID 0 1.00 1173.39 597.51 1.00 725.00 1091.00 1664.00 2505.00 ▅▃▇▇▁
EventID 0 1.00 6345.33 3081.50 1.00 3613.00 6988.00 9176.00 10340.00 ▃▃▁▇▇
Lat 0 1.00 59.66 3.58 51.64 58.33 59.40 59.60 71.39 ▁▇▃▁▁
Lon 0 1.00 -145.21 9.86 -176.95 -151.63 -150.22 -134.95 -130.99 ▁▁▇▁▇
Temp_C 1966 0.56 9.97 3.45 -0.92 7.40 10.50 12.50 28.70 ▂▇▇▁▁
Salinity 1988 0.56 24.07 14.20 -400.00 19.80 25.11 30.10 73.50 ▁▁▁▁▇
MeshSize 0 1.00 5.36 3.52 3.00 3.00 3.20 6.00 12.70 ▇▂▁▁▂

In particular we want to get a sense of how EventIDs are associated with SiteIDs and other variables placing them in space and time.

We see a lot of single-pull samples when events are grouped by SiteID and Date, i.e., a visit. However, we can’t assume all of those ‘visits’ are comparable in scope. Projects differed in structure and purpose, so what one researcher would consider a new site, another researcher might call a replicate event to be combined with other events within a single sample. I call this the “Site-Event” problem.

We see that the distribution of events becomes less severe when grouped by Date and Location, which tells me that some of those single-pull samples actually should be aggregated to be comparable to other multi-pull samples. However, grouping by location may not be accurate because of the loose definition of ‘Location’ (the levels of Location also seem to vary in scale). Since location info is tied to SiteID, we can’t just look at EventID’s and lat/lon. Instead, we’ll need to figure out a way to combine events at the SiteID level.

Basically, we are trying to combine SiteID’s that are currently unique but should have the same identifier, i.e., samples of the same beach. From there, we can define a VisitID as a sample of the same beach AND same day. Within a VisitID we have our EventID(s), i.e., single or multiple replicates of seines pulled on the same beach on the same day, which is the level of our catch data.

2.6 Using sf to solve the “Site-Event” problem

Here is where we need to decide on how to define a ‘beach’. In other words, what do we consider the spatial scale distinguishing one sample from another? From my personal seining, one of my larger-distanced sites was Anchor Point, where we’ve seined up to 800 m between first and last replicate. So in my opinion, 1 km would be reasonably conservative estimate to link replicate seines. Imagine seining by skiff- you could hop between replicates ~1 km down beach and still consider it the same site. In the below code, I define clusters of SiteID’s by grouping points that are within 1000 m of at least one other SiteID.

# Make a new tibble containing SiteID geometries:
sites.sf = events.2 %>%
  select(SiteID, Lat, Lon) %>%
  # Create sf object from lat/lon:
  st_as_sf(., coords = c("Lon", "Lat"), crs = 4326) %>% # WGS84
  # Project into Alaska Albers 3338
  st_transform(crs = 3338) %>%
  distinct()

# Define a buffer of 1km around each point
sites.sf.buf = st_buffer(sites.sf, dist = 1000)

# Create a tibble with clusters of overlapping buffer areas 
sites.sf.cl = sites.sf.buf %>%
  st_union() %>% # unite buffer areas
  st_cast(to = "POLYGON") %>% # turn them into polygon features
  st_as_sf() %>% # return geometry set features to sfc
  rownames_to_column(var = "Cluster") %>% # create col for cluster groups
  mutate(Cluster = as.factor(Cluster)) # use factor class

# Assign SiteID points to buffer area clusters
sites.sf.cl.join = st_join(sites.sf, sites.sf.cl, left = TRUE)

# Drop sf class from joined data
sites.cl = st_drop_geometry(sites.sf.cl.join)

# See what we got:
glimpse(sites.cl)
## Rows: 1,178
## Columns: 2
## $ SiteID  <dbl> 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18,…
## $ Cluster <fct> 327, 327, 327, 327, 327, 326, 326, 326, 326, 327, 327, 327, 32…

We now have a simple classification variable that can be joined back with the site information.

events.3 = left_join(events.2, sites.cl, by = "SiteID")

However, this only takes into account the spatial component of our larger goal of creating a unique VisitID identifier. Next, we’ll account for the sample dates where SiteID’s within a cluster match.

# Create a date-cluster tibble containing location and event info to be combined,
orig = select(events.3, Date, Cluster, SiteID, EventID, Lat, Lon)

# Nest the df by Date and Cluster,
orig.nest = orig %>%
  group_by(Date, Cluster) %>%
  nest(Sites = SiteID,
       Events = EventID,
       Lats = Lat,
       Lons = Lon) %>%
  ungroup()

We essentially created a our new sample identifier (visit) in nesting by date (day) and cluster (beach). To bring the data back to the EventID level, we need to deal with samples that now have multiple SiteID’s and locations, as well as preserve the EventID’s associated with the original SiteID. This means we will be overwriting data, which is why I called the above subset ‘orig’ and the below ‘new’.

new = orig.nest %>% 
  rowwise() %>% # Operates a row-at-a-time (per sample)
  mutate(SiteID.cl = min(Sites), 
         Lat.mean = mean(Lats$Lat),
         Lon.mean = mean(Lons$Lon)) %>%
  select(-c(Sites, Lats, Lons)) %>% # Remove original info in nested variables
  unite(col = VisitID, SiteID.cl, Date, sep = "_", remove = FALSE) %>% # create VisitID
  select(-c(Date, SiteID.cl, Cluster)) %>% # Remove info now captured by VisitID
  unnest_longer(Events) %>% # Unnest to the EventID level
  unpack(cols = Events) # change tibble back to vector

# Join our new identifier to the rest if the site data,
visits = left_join(new, events.3, by = "EventID") %>%
  mutate(Lat = Lat.mean, Lon = Lon.mean) %>% # Replace old lat/lon with new mean lat/lon
  select(-c(Lat.mean, Lon.mean))

# Check our our new data structure
head(visits, 10)
## # A tibble: 10 × 17
##    VisitID      EventID SiteID Date         Lat   Lon Region    Location Habitat
##    <chr>          <dbl>  <dbl> <date>     <dbl> <dbl> <chr>     <chr>    <chr>  
##  1 1_2001-07-24      15      1 2001-07-24  58.6 -135. Gulf of … southea… Kelp   
##  2 1_2001-07-24      16      2 2001-07-24  58.6 -135. Gulf of … southea… Kelp   
##  3 1_2001-07-24      17      3 2001-07-24  58.6 -135. Gulf of … southea… Kelp   
##  4 1_2001-07-24      18      4 2001-07-24  58.6 -135. Gulf of … southea… Sand-G…
##  5 1_2001-07-24      19      5 2001-07-24  58.6 -135. Gulf of … southea… Bedrock
##  6 1_2002-03-25      60      1 2002-03-25  58.6 -135. Gulf of … southea… Kelp   
##  7 1_2002-03-25      61      2 2002-03-25  58.6 -135. Gulf of … southea… Kelp   
##  8 1_2002-03-25      62      3 2002-03-25  58.6 -135. Gulf of … southea… Kelp   
##  9 1_2002-03-25      63      4 2002-03-25  58.6 -135. Gulf of … southea… Sand-G…
## 10 1_2002-03-25      64      5 2002-03-25  58.6 -135. Gulf of … southea… Bedrock
## # ℹ 8 more variables: TidalStage <chr>, Temp_C <dbl>, Salinity <dbl>,
## #   GearSpecific <chr>, ProjectName <chr>, PointOfContact <chr>,
## #   MeshSize <dbl>, Cluster <fct>

We can see in first 10 rows how the data stuctured by VisitID compares to the original. To select a beach identifier for the new VisitID, I simply went with the lowest SiteID of the nest list (no wrong answers here). SiteID is not very useful anymore, but I left it in the data anyway. The locations to be combined are at most a handful kms apart, so I figure a simple mean would suffice. Notice we now have distinct locations at the VisitID level- this is because different visits to the same beach might have had a different number of replicates. For example, we see that VisitID 1_2001-07-24 involved the first five rows (EventID’s 15-19), whereas, VisitID 1_2002-03-35 had an additional two events.

As a last step, let’s return to our frequency plots showing the number events per sample:

Wow. There are way less single-pull samples! Also, there are some interesting samples with replicates of 9, 10, and 12.

3 Wrangle catch data

Similar to our wrangle of site information, we want to hone the breadth of the catch data we use. At this stage, we don’t want to make any interpretations about the catch. Instead, the goal here is to weed out objectively unneeded and suspicious cases and ensure the consistency of the data (e.g., are all possible species named actual names?).

3.1 Set up

If it were necessary we would load additional packages, define directories, set options, source scripts. But we do not have any additional set up to do at this point.

3.2 Read in data

Nothing to do here.

3.3 Ediing taxonomy

skim(catch.1)
Data summary
Name catch.1
Number of rows 161621
Number of columns 9
_______________________
Column type frequency:
character 6
numeric 3
________________________
Group variables None

Variable type: character

skim_variable n_missing complete_rate min max empty n_unique whitespace
Sp_CommonName 0 1.00 7 31 0 195 0
Sp_ScientificName 0 1.00 7 38 0 190 0
Fam_CommonName 9 1.00 4 18 0 34 0
Fam_ScientificName 9 1.00 7 18 0 34 0
LengthType 77836 0.52 2 6 0 7 0
LifeStage 90497 0.44 3 8 0 6 0

Variable type: numeric

skim_variable n_missing complete_rate mean sd p0 p25 p50 p75 p100 hist
EventID 0 1.00 6017.42 3244.78 1 3199 6618 9192 10340 ▂▆▁▃▇
Unmeasured 0 1.00 12.80 512.01 0 0 0 0 99997 ▇▁▁▁▁
Length_mm 11535 0.93 98.33 74.16 4 48 76 124 930 ▇▁▁▁▁

One nice thing about skim() is that it quickly tells you where the information is missing and how much is missing. We see that there are nine cases of missing data at the family level. Something else that jumps out to me is that the number of unique species common names is not the same as the number of unique species scientific names.

I’ll also change the ‘Unmeasured’ variable to a count abundance, which may not be totally necessary but will make it easier for me to work with the data.

Another thing to point out is that less than half of the data appears to have life stage information. Furthermore, the data with length information is a little over 50%, which is a shame because we could have inferred life stage from length. At this point, it doesn’t seem worth while to toss out potentially half the data to include either of those variables. Although, I may return to this down the road.

First, let’s check out all the taxa we have in the data:

Taxa list - before edits

Species

Family

The discrepancy between species common and scientific names has to do with the cases where fish were given unidentified or juvenile labels. Nothing to fix here but we’ll just make sure to use scientific names in analyses.

I also notice a couple of cases where species are called something ‘or’ something else:

  1. Anisarchus medius or Lumpenus fabricii
  2. Osmeridae or Clupidae (misspelled)

Looking at the family table, we don’t see ‘Osmeridae or Clupeidae’, so I think that replacing these ‘or’ cases with their associated family scientific name should suffice.

Speaking of the family table, I see that those missing data show up as NA’s. We’ll take a look at it and make a judgment call on how to deal with the.

Last, I’ll also create a genus classification, which could end up being the level at which we do the analyses, depending on what that looks like.

I do not show results of the below code, but I provided an edited taxa list and see also comments for steps taken.

# Check out the cases where family is missing
filter(catch.1, is.na(Fam_ScientificName))
# Look up the visit info for these cases
filter(visits, EventID %in% catch.1$EventID[which(is.na(catch.1$Fam_ScientificName))])
# Check out everything else caught during these visits
filter(catch.1, EventID %in% catch.1$EventID[which(is.na(catch.1$Fam_ScientificName))])
# Since these nine NAs make up a small portion of their samples,
# The simplest solve here is to just remove the data.

# Create a new object after edits made to taxonomy
catch.2 = catch.1 %>%
  # Drop the nine instances of NA catch
  drop_na(Fam_ScientificName) %>%
  # Find the 'or' species and replace with Fam_ScientificName value
  mutate(Sp_ScientificName = ifelse(str_detect(Sp_ScientificName, pattern = " or "),
                                    Fam_ScientificName,
                                    Sp_ScientificName),
         # Create genus class by taking first word of Sp_ScientificName
         Gen_ScientificName = word(Sp_ScientificName, 1))

Taxa list - after edits

Species

Genus

Family

3.4 Abundance and occurence

3.4.1 ‘Unmeasured’ into ‘Count’

The below code changes the ‘Unmeasured’ parameter into ‘Count’ abundance:

catch.3 = mutate(catch.2, Count = ifelse(Unmeasured == 0,
                                         1,
                                         Unmeasured)) %>%
  select(-Unmeasured) # remove unused parameter

Now we can make graphs to see what kind of catch we have. For each taxa level, I’ll make raw abundance and frequency of occurrence graphs.

Visualizing catch data

Species

Genus

Family

# View the common names for rare Genuses,
# i.e., occuring in <0.25% of visits (6 or fewer visits)
# These include 34 different taxa
gen.freq.occur %>%
  filter(Perc_Occurrence < 0.25) %>%
  mutate(Gen_ScientificName = as.character(Gen_ScientificName)) %>%
  select(Gen_ScientificName) %>%
  arrange() %>%
  knitr::kable()
Gen_ScientificName
Gobiescocidae
Bathymasteridae
Scorpaenichthys
Odontopyxis
Anarrhichthys
Rimicola
Ronquilus
Cyclopteridae
Icelus
Chitonotus
Lampetra
Microstomus
Anisarchus
Gasterosteidae
Artediellus
Coregoninae
Lyopsetta
Stenodus
Thymallus
Aptocyclus
Dasycottus
Sigmistes
Lumpenella
Stenobrachius
Zoarcidae
Eopsetta
Anoplagonus
Cryptacanthodes
Gobiidae
Radulinus
# There are 10 families that occur in less than 1% of visits.
# We will consider these our extreme rare cases and remove them from the df,
fam.freq.occur %>%
  filter(Perc_Occurrence < 1) %>%
  mutate(Fam_ScientificName = as.character(Fam_ScientificName)) %>%
  select(Fam_ScientificName) %>%
  arrange() %>%
  knitr::kable()
Fam_ScientificName
Gobiescocidae
Cyclopteridae
Bathymasteridae
Anoplopomatidae
Anarhichadidae
Petromyzontidae
Zaproridae
Myctophidae
Zoarcidae
Cryptacanthodidae

LS0tCnRpdGxlOiAiTk9BQSBOZWFyc2hvcmUgRmlzaCBBdGxhcywgRGF0YSBXcmFuZ2xlIgphdXRob3I6ICJDaHJpcyBHdW8iCmRhdGU6ICJMYXN0IGNvbXBpbGVkIG9uIGByIGZvcm1hdChTeXMudGltZSgpLCAnJWQgJUIsICVZJylgIgpvdXRwdXQ6CiAgaHRtbF9kb2N1bWVudDoKICAgIHRvYzogVFJVRQogICAgdG9jX2RlcHRoOiAyCiAgICB0b2NfZmxvYXQ6CiAgICAgIGNvbGxhcHNlZDogRkFMU0UKICAgICAgcHJpbnQ6IEZBTFNFCiAgICBudW1iZXJfc2VjdGlvbnM6IFRSVUUKICAgIGNvZGVfZG93bmxvYWQ6IFRSVUUKLS0tCgpgYGB7ciBpbmNsdWRlID0gRkFMU0V9CmtuaXRyOjpvcHRzX2NodW5rJHNldChtZXNzYWdlID0gRkFMU0UpCmtuaXRyOjpvcHRzX2NodW5rJHNldCh3YXJuaW5nID0gRkFMU0UpCmtuaXRyOjpvcHRzX2NodW5rJHNldChzaXplID0gInNjcmlwdHNpemUiKQpgYGAKCkJ1aWx0IHdpdGggUiB2ZXJzaW9uIGByIGdldFJ2ZXJzaW9uKClgLgoKIyBJbnRybwoKSGVyZSwgSSBkb2N1bWVudCB0aGUgc3RlcHMgdGFrZW4gaW4gd3JhbmdsaW5nIHRoZSBOT0FBIE5lYXJzaG9yZSBGaXNoIEF0bGFzIChORkEpLiBUaGlzIHJlcGxhY2VzIHRoZSBzZXBhcmF0ZSBzY3JpcHRzIChuYW1lZCAiLi9GaXNoQXRsYXNcXyNcX2Rlc2NyaXB0aW9uLlIiKSBhbmQgY29tYmluZXMgdGhlbSBpbnRvIGEgc2luZ2xlLCBzdHJlYW1saW5lZCBSIG1hcmtkb3duIGZpbGUuIFRoZXNlIFIgc2NyaXB0cyB3aWxsIGJlIGFyY2hpdmVkIGluIGNhc2UgSSBuZWVkIHRvIHJlcGxpY2F0ZSBvbGRlciBtZXRob2RzLiBOYW1lbHksIEkgdXNlZCB0byB1c2UgKipyZ2RhbCoqIHBhY2thZ2UgZm9yIHNvbWUgc3RlcHMgaW4gdGhlIHdyYW5nbGluZyBwcm9jZXNzIGJ1dCB0aGF0IHBhY2thZ2Ugc3RvcHBlZCBiZWluZyBzdXBwb3J0ZWQgY2EuIE9jdCAyMDIzIGluIGxpZXUgb2YgdGhlICoqc2YqKiBwYWNrYWdlLgoKTGluayB0byB0aGUgTkZBIGNhbiBiZSBmb3VuZCBoZXJlLCA8aHR0cHM6Ly9hbGFza2FmaXNoZXJpZXMubm9hYS5nb3YvbWFwcGluZy9zei8+LiBUaGUgZW50aXJlIGRhdGFiYXNlIGNhbiBiZSBkb3dubG9hZGVkIGFzIGEgLmNzdiBmaWxlLCB3aGljaCBpcyB3aGF0IEkgdXNlIGhlcmUuIEhvd2V2ZXIsIEkgaGF2ZSBhZGRpdGlvbmFsIGRhdGEgZmlsZXMgdGhhdCB3ZXJlIGVpdGhlciBzaGFyZWQgd2l0aCBtZSBieSBORkEgY29udGVjdCBtYW5hZ2VycyBvciBtb2RpZmllZCBmcm9tIHRoZSBOT0FBIE5GQSBkYXRhIGRpcmVjdG9yeSBmb3IgZWFzZSBvZiB1cGxvYWRpbmcgaW50byBSLiBJIHRyeSB0byBkb2N1bWVudCB0aGVzZSBjYXNlcyBpbiB0aGUgZnV0dXJlLgoKIyBXcmFuZ2xlIHNpdGUgZGF0YQoKVGhlIGlkZWEgd2l0aCB0aGlzIGluaXRpYWwgc3RlcCBpcyB0byBkZWZpbmUgdGhlIHNjYWxlIG9mIG91ciBzYW1wbGVzLiBDb250cmlidXRvcnMgdG8gdGhlIE5GQSBkaWQgbm90IGRlZmluZSB0aGVpciBTaXRlSUQncyBhbmQgRXZlbnRJRCdzIHNpbWlsYXIgdG8gZWFjaCBvdGhlciwgc28gd2UnbGwgbmVlZCB0byBkaXN0aW5ndWlzaCBiZXR3ZWVuIFNpdGVJRCB2cyBFdmVudElEIGFuZCBtYWtlIHN1cmUgaXQncyBjb25zaXN0ZW50IHRocm91Z2hvdXQgdGhlIGRhdGEuCgpJIGRlZmluZSBhIFNpdGVJRCBhcyAqYSB1bmlxdWUgcGxhY2UgaW4gc3BhY2UqLCBpLmUuLCBhbGwgc2FtcGxlcyBmcm9tIHRoZSBzYW1lICdiZWFjaCcgKHdpbGwgYmUgZGVmaW5lZCBsYXRlciBvbikuIEFuIEV2ZW50SUQgaXMgKmEgdW5pcXVlIHB1bGwgb2YgdGhlIGJlYWNoIHNlaW5lKi4gU28gdGhlcmUgY2FuIGJlIHNpbmdsZSBvciBtdWx0aXBsZSBFdmVudElEJ3MgYXNzb2NpYXRlZCB3aXRoIGEgU2l0ZUlELiBBZGRpdGlvbmFsbHksIEkgaW50cm9kdWNlIGEgVmlzaXRJRCwgd2hpY2ggYWRkcyBhIHRlbXBvcmFsIGRpc3RpbmN0aW9uIGFtb25nIFNpdGVJRCdzLCBpLmUuLCAqYWxsIGV2ZW50cyBmcm9tIHRoZSBzYW1lIGJlYWNoIG9uIHRoZSBzYW1lIGRheSouCgojIyBTZXQgdXAKCkxvYWQgcmVxdWlyZWQgcGFja2FnZXMsIGRlZmluZSBkaXJlY3RvcnksIHNldCBvcHRpb25zLCBzb3VyY2Ugc2NyaXB0czoKCmBgYHtyfQojIFBhY2thZ2VzCmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KGx1YnJpZGF0ZSkKbGlicmFyeShoZXJlKQpsaWJyYXJ5KHNraW1yKQpsaWJyYXJ5KHNmKQoKIyBEaXJlY3RvcnkKd2QgPSBoZXJlKCkKZGlycyA9IHdkICU+JSBsaXN0LmZpbGVzKCkgJT4lIHN0cl9zdWJzZXQocGF0dGVybiA9ICJeUkVBRE1FfF5MSUNFTlNFfC5tZCR8LlJwcm9qJCIsIG5lZ2F0ZSA9IFRSVUUpCmZvciAoaSBpbiBzZXFfYWxvbmcoZGlycykpIHsKICBuYW1lID0gc3RyX3JlcGxhY2VfYWxsKGRpcnNbaV0sICJeIiwgImRpci4iKQogIHBhdGggPSBzdHJfcmVwbGFjZV9hbGwoZGlyc1tpXSwgIl4iLCBzdHJfYyh3ZCwgIi8iKSkKICBhc3NpZ24obmFtZSwgcGF0aCkKICBybShuYW1lLCBwYXRoLCBpKQp9CgojIE9wdGlvbnMKCiMgU291cmNlCgpgYGAKCiMjIFJlYWQgaW4gZGF0YQoKYGBge3J9CmRhdGEgPSByZWFkX2NzdihmaWxlLnBhdGgoZGlyLmRhdGEsICJGaXNoQXRsYXNfQmVhY2hTZWluZXNfMjAyMi4wNy4xMi5jc3YiKSwKICAgICAgICAgICAgICAgc2hvd19jb2xfdHlwZXMgPSBGQUxTRSkKR2Vhckxvb2t1cCA9IHJlYWRfY3N2KGZpbGUucGF0aChkaXIuZGF0YSwgIkZpc2hBdGxhc0V4cGFuc2lvbl8wNDA0MjJfTG9va3VwX0dlYXIuY3N2IiksCiAgICAgICAgICAgICAgICAgICAgICBzaG93X2NvbF90eXBlcyA9IEZBTFNFKQpgYGAKCiMjIFNlcGFyYXRlIHNpdGUgYW5kIHNwZWNpZXMgaW5mbwoKQXMgaW4gdHlwaWNhbCBjb21tdW5pdHkgZGF0YSAoc2l0ZSB4IHNwZWNpZXMpLCB3ZSBoYXZlIGluZm8gdGllZCB0byBzaXRlIGRlc2NyaXB0aW9uIGFuZCBpbmZvIHRpZWQgdG8gdGF4YS4gTm90aWNlIHRoYXQgRXZlbnRJRCBpcyB0aGUgJ2xvb2stdXAnIGtleSB1c2VkIGluIGJvdGggZGF0YSBzZXRzLgoKYGBge3J9CmV2ZW50cy4xID0gZGF0YSAlPiUKICBzZWxlY3QoU2l0ZUlELCBFdmVudElELAogICAgICAgICBEYXRlLAogICAgICAgICBMYXQsIExvbiA9IExvbmcsIFJlZ2lvbiwgTG9jYXRpb24sCiAgICAgICAgIEhhYml0YXQsIFRpZGFsU3RhZ2UsIFRlbXBfQywgU2FsaW5pdHksCiAgICAgICAgIEdlYXJTcGVjaWZpYywgUHJvamVjdE5hbWUsIFBvaW50T2ZDb250YWN0KSAlPiUKICBtdXRhdGUoRGF0ZSA9IG1keShEYXRlKSkgJT4lICMgcmUtZm9ybWF0IGRhdGUKICBkaXN0aW5jdCgpCgpjYXRjaC4xID0gZGF0YSAlPiUKICBzZWxlY3QoRXZlbnRJRCwKICAgICAgICAgU3BfQ29tbW9uTmFtZSwgU3BfU2NpZW50aWZpY05hbWUsIEZhbV9Db21tb25OYW1lLCBGYW1fU2NpZW50aWZpY05hbWUsCiAgICAgICAgIFVubWVhc3VyZWQsIExlbmd0aF9tbSwgTGVuZ3RoVHlwZSwgTGlmZVN0YWdlKQpgYGAKCiMjIENvbnNpZGVyaW5nIGdlYXIgdHlwZQoKVGhlIE5GQSB3ZWJzaXRlIG1ha2VzIGl0IGVhc3kgdG8gZmlsdGVyIGZvciBnZWFyIHR5cGUsIHNvIEkgZG9uZSBwcmlvciB0byBkb3dubG9hZGluZyB0aGUgLmNzdiBmaWxlLiBIb3dldmVyLCB3ZSdsbCB3YW50IHRvIGFjY291bnQgZm9yIGRpZmZlcmVuY2VzIGFtb25nIHRoZSBiZWFjaCBzZWluZXMgdXNlZC0gbmFtZWx5IHRoZSBuZXQgbWVzaCBzaXplLiBUaGlzIGluZm9ybWF0aW9uIGlzIGNvbnRhaW5lZCBpbiB0aGUgbWV0YWRhdGEgZm9yIHRoZSBORkEgZGF0YWJhc2UgYW5kIGNhbiBiZSBhY2Nlc3NlZCBieSByZXF1ZXN0IGZyb20gdGhlIGRhdGFiYXNlIG1hbmFnZXJzIChmaWxlICpGaXNoQXRsYXNFeHBhbnNpb25fMDQwNDIyX0xvb2t1cF9HZWFyLmNzdiopLiBBIGZldyBpbnN0YW5jZXMgb2YgbWlzc2luZyBtZXNoIHNpemUgaW5mb3JtYXRpb24gd2FzIHByb3ZpZGVkIGJ5IGVtYWlsIHJlcXVlc3QgdG8gdGhlIGFzc29jaWF0ZWQgcG9pbnQgb2YgY29udGFjdCBmb3IgdGhhdCBjb250cmlidXRlZCBkYXRhLiBSZXN1bHRzIGZyb20gdGhlIGJlbG93IGNvZGUgYXJlIGhpZGRlbiBidXQgY29tbWVudHMgZXhwbGFpbiB3aGF0IGRlY2lzaW9ucyB3ZXJlIG1hZGUuCgpgYGB7ciByZXN1bHRzID0gJ2hpZGUnfQojIEpvaW4gdGhlIG1lc2ggc2l6ZSBpbmZvIGZyb20gb3VyIGdlYXIgZGF0YSB0byB0aGUgc2l0ZSBpbmZvcm1hdGlvbgpnZWFyLjEgPSBsZWZ0X2pvaW4oZXZlbnRzLjEsIHNlbGVjdChHZWFyTG9va3VwLCBHZWFyU3BlY2lmaWMsIE1lc2hTaXplKSwgYnkgPSAiR2VhclNwZWNpZmljIikKCiMgU3Vic2V0IGZvciBnZWFyIHJlbGF0ZWQgaW5mbyBhbmQgdGFrZSBhIGxvb2sKZ2Vhci4yID0gc2VsZWN0KGdlYXIuMSwgRXZlbnRJRCwgTWVzaFNpemUsIEdlYXJTcGVjaWZpYywgUHJvamVjdE5hbWUsIFBvaW50T2ZDb250YWN0KQpnbGltcHNlKGdlYXIuMikKIyBIb3cgbWFueSBkaWZmZXJlbnQgbWVzaCBzaXplcyBhcmUgdGhlcmU/CmdlYXIuMiRNZXNoU2l6ZSAlPiUgdW5pcXVlKCkKCiMgTG9va3MgbGlrZSB3ZSBoYXZlIGFuIE5BIHRvIHRha2UgY2FyZSBvZi4uLgpmaWx0ZXIoZ2Vhci4yLCBpcy5uYShNZXNoU2l6ZSkpCiMgVGhlc2UgYXJlIGFsbCBmcm9tIHRoZSBHT0FJRVJQIHByb2plY3QgKFBJIE9sYXYgT3Jtc2V0aCkuIFRoZSByZXBvcnQgZnJvbSB0aGF0IHByb2plY3QgcmVwb3J0cyA2IG1tIHN0cmV0Y2hlZCBtZXNoLgojIFdlJ2xsIGp1c3QgZ28gYWhlYWQgYW5kIGZpeCBpdCBoZXJlIGluc3RlYWQgb2YgY2hhbmdpbmcgdGhlIEdlYXJMb29rdXAgZmlsZSwKZ2Vhci4zID0gbXV0YXRlKGdlYXIuMiwgTWVzaFNpemUgPSBpZmVsc2UoaXMubmEoTWVzaFNpemUpLCA2LCBNZXNoU2l6ZSkpCgojIENoZWNrIG91ciBtZXNoIHNpemVzIGFnYWluLApnZWFyLjMkTWVzaFNpemUgJT4lIHVuaXF1ZSgpCiMgR3JlYXQsIG5vIG1vcmUgTkEncy4KCiMgV2UgY2FuIG5vdyBqb2luIHRoaXMgaW5mb3JtYXRpb24gd2l0aCBvdXIgc2l0ZSBkYXRhLApldmVudHMuMiA9IGxlZnRfam9pbihldmVudHMuMSwgc2VsZWN0KGdlYXIuMywgRXZlbnRJRCwgTWVzaFNpemUpLCBieSA9ICJFdmVudElEIikKYGBgCgojIyBUaGUgIlNpdGUtRXZlbnQiIHByb2JsZW0KClRoZSBjYXRjaCBkYXRhIGlzIHRpZWQgdG8gdW5pcXVlIEV2ZW50SURzIHdoaWNoIGFyZSBhc3NvY2lhdGVkIHdpdGggc3BhdGlhbGx5IGV4cGxpY2l0IFNpdGVJRHMgKGVhY2ggU2l0ZUlEIGhhcyBhIHVuaXF1ZSBsYXQvbG9uKS4gQ29uc2lkZXJpbmcgdGhhdCB0aGUgZGF0YSBoYXMgYmVlbiBjb250cmlidXRlZCBieSBtdWx0aXBsZSByZXNlYXJjaGVycy9wcm9qZWN0cywgd2UnbGwgbmVlZCB0byBtYWtlIHN1cmUgd2UgaGF2ZSBjb25zaXN0ZW50IHNpdGVzIGFuZCBldmVudHMgYWNyb3NzIHRoZSB3aG9sZSBkYXRhc2V0LiBBZnRlciB0aGF0IHdlIGNhbiB0YWNrbGUgdGhlIGNhdGNoIGRhdGEuIEZpcnN0LCB3ZSB0YWtlIGEgbG9vayBhdCBhbGwgb2YgdGhlIHNpdGUgZGF0YSB3aXRoICoqc2tpbXI6OnNraW0oKSoqLgoKYGBge3J9CnNraW0oZXZlbnRzLjIpCmBgYAoKSW4gcGFydGljdWxhciB3ZSB3YW50IHRvIGdldCBhIHNlbnNlIG9mIGhvdyBFdmVudElEcyBhcmUgYXNzb2NpYXRlZCB3aXRoIFNpdGVJRHMgYW5kIG90aGVyIHZhcmlhYmxlcyBwbGFjaW5nIHRoZW0gaW4gc3BhY2UgYW5kIHRpbWUuCgpgYGB7ciBlY2hvID0gRkFMU0V9CmV2ZW50cy4yICU+JSBncm91cF9ieShTaXRlSUQsIERhdGUpICU+JSBzdW1tYXJpc2UoZXZlbnRzID0gbl9kaXN0aW5jdChFdmVudElEKSkgJT4lCiAgZ2dwbG90KGRhdGEgPSAuLCBhZXMoeCA9IGV2ZW50cykpICsKICBnZW9tX2JhcihhZXMoeSA9IGFmdGVyX3N0YXQoY291bnQpKSkgKwogIGdlb21fdGV4dChzdGF0ID0gJ2NvdW50JywgYWVzKGxhYmVsID0gYWZ0ZXJfc3RhdChjb3VudCkpLCB2anVzdCA9IC0wLjUpICsKICBsYWJzKHRpdGxlID0gIkZyZXF1ZW5jeSBvZiBTaXRlSUQtRGF0ZSBwYWlycyBieSAjIG9mIGV2ZW50cyAocmVwbGljYXRlcykiKQoKZXZlbnRzLjIgJT4lIGdyb3VwX2J5KERhdGUsIExvY2F0aW9uKSAlPiUgc3VtbWFyaXNlKGV2ZW50cyA9IG5fZGlzdGluY3QoRXZlbnRJRCkpICU+JQogIGdncGxvdChkYXRhID0gLiwgYWVzKHggPSBldmVudHMpKSArCiAgZ2VvbV9iYXIoYWVzKHkgPSBhZnRlcl9zdGF0KGNvdW50KSkpICsKICBnZW9tX3RleHQoc3RhdCA9ICdjb3VudCcsIGFlcyhsYWJlbCA9IGFmdGVyX3N0YXQoY291bnQpKSwgdmp1c3QgPSAtMC41KSArCiAgbGFicyh0aXRsZSA9ICJGcmVxdWVuY3kgb2YgRGF0ZS1Mb2NhdGlvbiBwYWlycyB3aXRoICMgb2YgZXZlbnRzIChyZXBsaWNhdGVzKSIpCmBgYAoKV2Ugc2VlIGEgbG90IG9mIHNpbmdsZS1wdWxsIHNhbXBsZXMgd2hlbiBldmVudHMgYXJlIGdyb3VwZWQgYnkgU2l0ZUlEIGFuZCBEYXRlLCBpLmUuLCBhICp2aXNpdCouIEhvd2V2ZXIsIHdlIGNhbid0IGFzc3VtZSBhbGwgb2YgdGhvc2UgJ3Zpc2l0cycgYXJlIGNvbXBhcmFibGUgaW4gc2NvcGUuIFByb2plY3RzIGRpZmZlcmVkIGluIHN0cnVjdHVyZSBhbmQgcHVycG9zZSwgc28gd2hhdCBvbmUgcmVzZWFyY2hlciB3b3VsZCBjb25zaWRlciBhIG5ldyBzaXRlLCBhbm90aGVyIHJlc2VhcmNoZXIgbWlnaHQgY2FsbCBhIHJlcGxpY2F0ZSBldmVudCB0byBiZSBjb21iaW5lZCB3aXRoIG90aGVyIGV2ZW50cyB3aXRoaW4gYSBzaW5nbGUgc2FtcGxlLiBJIGNhbGwgdGhpcyB0aGUgIlNpdGUtRXZlbnQiIHByb2JsZW0uCgpXZSBzZWUgdGhhdCB0aGUgZGlzdHJpYnV0aW9uIG9mIGV2ZW50cyBiZWNvbWVzIGxlc3Mgc2V2ZXJlIHdoZW4gZ3JvdXBlZCBieSBEYXRlIGFuZCBMb2NhdGlvbiwgd2hpY2ggdGVsbHMgbWUgdGhhdCBzb21lIG9mIHRob3NlIHNpbmdsZS1wdWxsIHNhbXBsZXMgYWN0dWFsbHkgc2hvdWxkIGJlIGFnZ3JlZ2F0ZWQgdG8gYmUgY29tcGFyYWJsZSB0byBvdGhlciBtdWx0aS1wdWxsIHNhbXBsZXMuIEhvd2V2ZXIsIGdyb3VwaW5nIGJ5IGxvY2F0aW9uIG1heSBub3QgYmUgYWNjdXJhdGUgYmVjYXVzZSBvZiB0aGUgbG9vc2UgZGVmaW5pdGlvbiBvZiAnTG9jYXRpb24nICh0aGUgbGV2ZWxzIG9mIExvY2F0aW9uIGFsc28gc2VlbSB0byB2YXJ5IGluIHNjYWxlKS4gU2luY2UgbG9jYXRpb24gaW5mbyBpcyB0aWVkIHRvIFNpdGVJRCwgd2UgY2FuJ3QganVzdCBsb29rIGF0IEV2ZW50SUQncyBhbmQgbGF0L2xvbi4gSW5zdGVhZCwgd2UnbGwgbmVlZCB0byBmaWd1cmUgb3V0IGEgd2F5IHRvIGNvbWJpbmUgZXZlbnRzIGF0IHRoZSBTaXRlSUQgbGV2ZWwuCgpCYXNpY2FsbHksIHdlIGFyZSB0cnlpbmcgdG8gY29tYmluZSBTaXRlSUQncyB0aGF0IGFyZSBjdXJyZW50bHkgdW5pcXVlIGJ1dCBzaG91bGQgaGF2ZSB0aGUgc2FtZSBpZGVudGlmaWVyLCBpLmUuLCBzYW1wbGVzIG9mIHRoZSBzYW1lIGJlYWNoLiBGcm9tIHRoZXJlLCB3ZSBjYW4gZGVmaW5lIGEgVmlzaXRJRCBhcyBhIHNhbXBsZSBvZiB0aGUgc2FtZSBiZWFjaCBBTkQgc2FtZSBkYXkuIFdpdGhpbiBhIFZpc2l0SUQgd2UgaGF2ZSBvdXIgRXZlbnRJRChzKSwgaS5lLiwgc2luZ2xlIG9yIG11bHRpcGxlIHJlcGxpY2F0ZXMgb2Ygc2VpbmVzIHB1bGxlZCBvbiB0aGUgc2FtZSBiZWFjaCBvbiB0aGUgc2FtZSBkYXksIHdoaWNoIGlzIHRoZSBsZXZlbCBvZiBvdXIgY2F0Y2ggZGF0YS4KCiMjIFVzaW5nICoqc2YqKiB0byBzb2x2ZSB0aGUgIlNpdGUtRXZlbnQiIHByb2JsZW0KCkhlcmUgaXMgd2hlcmUgd2UgbmVlZCB0byBkZWNpZGUgb24gaG93IHRvIGRlZmluZSBhICdiZWFjaCcuIEluIG90aGVyIHdvcmRzLCB3aGF0IGRvIHdlIGNvbnNpZGVyIHRoZSBzcGF0aWFsIHNjYWxlIGRpc3Rpbmd1aXNoaW5nIG9uZSBzYW1wbGUgZnJvbSBhbm90aGVyPyBGcm9tIG15IHBlcnNvbmFsIHNlaW5pbmcsIG9uZSBvZiBteSBsYXJnZXItZGlzdGFuY2VkIHNpdGVzIHdhcyBBbmNob3IgUG9pbnQsIHdoZXJlIHdlJ3ZlIHNlaW5lZCB1cCB0byA4MDAgbSBiZXR3ZWVuIGZpcnN0IGFuZCBsYXN0IHJlcGxpY2F0ZS4gU28gaW4gbXkgb3BpbmlvbiwgMSBrbSB3b3VsZCBiZSByZWFzb25hYmx5IGNvbnNlcnZhdGl2ZSBlc3RpbWF0ZSB0byBsaW5rIHJlcGxpY2F0ZSBzZWluZXMuIEltYWdpbmUgc2VpbmluZyBieSBza2lmZi0geW91IGNvdWxkIGhvcCBiZXR3ZWVuIHJlcGxpY2F0ZXMgXH4xIGttIGRvd24gYmVhY2ggYW5kIHN0aWxsIGNvbnNpZGVyIGl0IHRoZSBzYW1lIHNpdGUuIEluIHRoZSBiZWxvdyBjb2RlLCBJIGRlZmluZSBjbHVzdGVycyBvZiBTaXRlSUQncyBieSBncm91cGluZyBwb2ludHMgdGhhdCBhcmUgd2l0aGluIDEwMDAgbSBvZiBhdCBsZWFzdCBvbmUgb3RoZXIgU2l0ZUlELgoKYGBge3J9CiMgTWFrZSBhIG5ldyB0aWJibGUgY29udGFpbmluZyBTaXRlSUQgZ2VvbWV0cmllczoKc2l0ZXMuc2YgPSBldmVudHMuMiAlPiUKICBzZWxlY3QoU2l0ZUlELCBMYXQsIExvbikgJT4lCiAgIyBDcmVhdGUgc2Ygb2JqZWN0IGZyb20gbGF0L2xvbjoKICBzdF9hc19zZiguLCBjb29yZHMgPSBjKCJMb24iLCAiTGF0IiksIGNycyA9IDQzMjYpICU+JSAjIFdHUzg0CiAgIyBQcm9qZWN0IGludG8gQWxhc2thIEFsYmVycyAzMzM4CiAgc3RfdHJhbnNmb3JtKGNycyA9IDMzMzgpICU+JQogIGRpc3RpbmN0KCkKCiMgRGVmaW5lIGEgYnVmZmVyIG9mIDFrbSBhcm91bmQgZWFjaCBwb2ludApzaXRlcy5zZi5idWYgPSBzdF9idWZmZXIoc2l0ZXMuc2YsIGRpc3QgPSAxMDAwKQoKIyBDcmVhdGUgYSB0aWJibGUgd2l0aCBjbHVzdGVycyBvZiBvdmVybGFwcGluZyBidWZmZXIgYXJlYXMgCnNpdGVzLnNmLmNsID0gc2l0ZXMuc2YuYnVmICU+JQogIHN0X3VuaW9uKCkgJT4lICMgdW5pdGUgYnVmZmVyIGFyZWFzCiAgc3RfY2FzdCh0byA9ICJQT0xZR09OIikgJT4lICMgdHVybiB0aGVtIGludG8gcG9seWdvbiBmZWF0dXJlcwogIHN0X2FzX3NmKCkgJT4lICMgcmV0dXJuIGdlb21ldHJ5IHNldCBmZWF0dXJlcyB0byBzZmMKICByb3duYW1lc190b19jb2x1bW4odmFyID0gIkNsdXN0ZXIiKSAlPiUgIyBjcmVhdGUgY29sIGZvciBjbHVzdGVyIGdyb3VwcwogIG11dGF0ZShDbHVzdGVyID0gYXMuZmFjdG9yKENsdXN0ZXIpKSAjIHVzZSBmYWN0b3IgY2xhc3MKCiMgQXNzaWduIFNpdGVJRCBwb2ludHMgdG8gYnVmZmVyIGFyZWEgY2x1c3RlcnMKc2l0ZXMuc2YuY2wuam9pbiA9IHN0X2pvaW4oc2l0ZXMuc2YsIHNpdGVzLnNmLmNsLCBsZWZ0ID0gVFJVRSkKCiMgRHJvcCBzZiBjbGFzcyBmcm9tIGpvaW5lZCBkYXRhCnNpdGVzLmNsID0gc3RfZHJvcF9nZW9tZXRyeShzaXRlcy5zZi5jbC5qb2luKQoKIyBTZWUgd2hhdCB3ZSBnb3Q6CmdsaW1wc2Uoc2l0ZXMuY2wpCmBgYAoKV2Ugbm93IGhhdmUgYSBzaW1wbGUgY2xhc3NpZmljYXRpb24gdmFyaWFibGUgdGhhdCBjYW4gYmUgam9pbmVkIGJhY2sgd2l0aCB0aGUgc2l0ZSBpbmZvcm1hdGlvbi4KCmBgYHtyfQpldmVudHMuMyA9IGxlZnRfam9pbihldmVudHMuMiwgc2l0ZXMuY2wsIGJ5ID0gIlNpdGVJRCIpCmBgYAoKSG93ZXZlciwgdGhpcyBvbmx5IHRha2VzIGludG8gYWNjb3VudCB0aGUgc3BhdGlhbCBjb21wb25lbnQgb2Ygb3VyIGxhcmdlciBnb2FsIG9mIGNyZWF0aW5nIGEgdW5pcXVlIFZpc2l0SUQgaWRlbnRpZmllci4gTmV4dCwgd2UnbGwgYWNjb3VudCBmb3IgdGhlIHNhbXBsZSBkYXRlcyB3aGVyZSBTaXRlSUQncyB3aXRoaW4gYSBjbHVzdGVyIG1hdGNoLgoKYGBge3J9CiMgQ3JlYXRlIGEgZGF0ZS1jbHVzdGVyIHRpYmJsZSBjb250YWluaW5nIGxvY2F0aW9uIGFuZCBldmVudCBpbmZvIHRvIGJlIGNvbWJpbmVkLApvcmlnID0gc2VsZWN0KGV2ZW50cy4zLCBEYXRlLCBDbHVzdGVyLCBTaXRlSUQsIEV2ZW50SUQsIExhdCwgTG9uKQoKIyBOZXN0IHRoZSBkZiBieSBEYXRlIGFuZCBDbHVzdGVyLApvcmlnLm5lc3QgPSBvcmlnICU+JQogIGdyb3VwX2J5KERhdGUsIENsdXN0ZXIpICU+JQogIG5lc3QoU2l0ZXMgPSBTaXRlSUQsCiAgICAgICBFdmVudHMgPSBFdmVudElELAogICAgICAgTGF0cyA9IExhdCwKICAgICAgIExvbnMgPSBMb24pICU+JQogIHVuZ3JvdXAoKQpgYGAKCldlIGVzc2VudGlhbGx5IGNyZWF0ZWQgYSBvdXIgbmV3IHNhbXBsZSBpZGVudGlmaWVyICh2aXNpdCkgaW4gbmVzdGluZyBieSBkYXRlIChkYXkpIGFuZCBjbHVzdGVyIChiZWFjaCkuIFRvIGJyaW5nIHRoZSBkYXRhIGJhY2sgdG8gdGhlIEV2ZW50SUQgbGV2ZWwsIHdlIG5lZWQgdG8gZGVhbCB3aXRoIHNhbXBsZXMgdGhhdCBub3cgaGF2ZSBtdWx0aXBsZSBTaXRlSUQncyBhbmQgbG9jYXRpb25zLCBhcyB3ZWxsIGFzIHByZXNlcnZlIHRoZSBFdmVudElEJ3MgYXNzb2NpYXRlZCB3aXRoIHRoZSBvcmlnaW5hbCBTaXRlSUQuIFRoaXMgbWVhbnMgd2Ugd2lsbCBiZSBvdmVyd3JpdGluZyBkYXRhLCB3aGljaCBpcyB3aHkgSSBjYWxsZWQgdGhlIGFib3ZlIHN1YnNldCAnb3JpZycgYW5kIHRoZSBiZWxvdyAnbmV3Jy4KCmBgYHtyfQpuZXcgPSBvcmlnLm5lc3QgJT4lIAogIHJvd3dpc2UoKSAlPiUgIyBPcGVyYXRlcyBhIHJvdy1hdC1hLXRpbWUgKHBlciBzYW1wbGUpCiAgbXV0YXRlKFNpdGVJRC5jbCA9IG1pbihTaXRlcyksIAogICAgICAgICBMYXQubWVhbiA9IG1lYW4oTGF0cyRMYXQpLAogICAgICAgICBMb24ubWVhbiA9IG1lYW4oTG9ucyRMb24pKSAlPiUKICBzZWxlY3QoLWMoU2l0ZXMsIExhdHMsIExvbnMpKSAlPiUgIyBSZW1vdmUgb3JpZ2luYWwgaW5mbyBpbiBuZXN0ZWQgdmFyaWFibGVzCiAgdW5pdGUoY29sID0gVmlzaXRJRCwgU2l0ZUlELmNsLCBEYXRlLCBzZXAgPSAiXyIsIHJlbW92ZSA9IEZBTFNFKSAlPiUgIyBjcmVhdGUgVmlzaXRJRAogIHNlbGVjdCgtYyhEYXRlLCBTaXRlSUQuY2wsIENsdXN0ZXIpKSAlPiUgIyBSZW1vdmUgaW5mbyBub3cgY2FwdHVyZWQgYnkgVmlzaXRJRAogIHVubmVzdF9sb25nZXIoRXZlbnRzKSAlPiUgIyBVbm5lc3QgdG8gdGhlIEV2ZW50SUQgbGV2ZWwKICB1bnBhY2soY29scyA9IEV2ZW50cykgIyBjaGFuZ2UgdGliYmxlIGJhY2sgdG8gdmVjdG9yCgojIEpvaW4gb3VyIG5ldyBpZGVudGlmaWVyIHRvIHRoZSByZXN0IGlmIHRoZSBzaXRlIGRhdGEsCnZpc2l0cyA9IGxlZnRfam9pbihuZXcsIGV2ZW50cy4zLCBieSA9ICJFdmVudElEIikgJT4lCiAgbXV0YXRlKExhdCA9IExhdC5tZWFuLCBMb24gPSBMb24ubWVhbikgJT4lICMgUmVwbGFjZSBvbGQgbGF0L2xvbiB3aXRoIG5ldyBtZWFuIGxhdC9sb24KICBzZWxlY3QoLWMoTGF0Lm1lYW4sIExvbi5tZWFuKSkKCiMgQ2hlY2sgb3VyIG91ciBuZXcgZGF0YSBzdHJ1Y3R1cmUKaGVhZCh2aXNpdHMsIDEwKQpgYGAKCldlIGNhbiBzZWUgaW4gZmlyc3QgMTAgcm93cyBob3cgdGhlIGRhdGEgc3R1Y3R1cmVkIGJ5IFZpc2l0SUQgY29tcGFyZXMgdG8gdGhlIG9yaWdpbmFsLiBUbyBzZWxlY3QgYSBiZWFjaCBpZGVudGlmaWVyIGZvciB0aGUgbmV3IFZpc2l0SUQsIEkgc2ltcGx5IHdlbnQgd2l0aCB0aGUgbG93ZXN0IFNpdGVJRCBvZiB0aGUgbmVzdCBsaXN0IChubyB3cm9uZyBhbnN3ZXJzIGhlcmUpLiBTaXRlSUQgaXMgbm90IHZlcnkgdXNlZnVsIGFueW1vcmUsIGJ1dCBJIGxlZnQgaXQgaW4gdGhlIGRhdGEgYW55d2F5LiBUaGUgbG9jYXRpb25zIHRvIGJlIGNvbWJpbmVkIGFyZSBhdCBtb3N0IGEgaGFuZGZ1bCBrbXMgYXBhcnQsIHNvIEkgZmlndXJlIGEgc2ltcGxlIG1lYW4gd291bGQgc3VmZmljZS4gTm90aWNlIHdlIG5vdyBoYXZlIGRpc3RpbmN0IGxvY2F0aW9ucyBhdCB0aGUgVmlzaXRJRCBsZXZlbC0gdGhpcyBpcyBiZWNhdXNlIGRpZmZlcmVudCB2aXNpdHMgdG8gdGhlIHNhbWUgYmVhY2ggbWlnaHQgaGF2ZSBoYWQgYSBkaWZmZXJlbnQgbnVtYmVyIG9mIHJlcGxpY2F0ZXMuIEZvciBleGFtcGxlLCB3ZSBzZWUgdGhhdCBWaXNpdElEIDFfMjAwMS0wNy0yNCBpbnZvbHZlZCB0aGUgZmlyc3QgZml2ZSByb3dzIChFdmVudElEJ3MgMTUtMTkpLCB3aGVyZWFzLCBWaXNpdElEIDFfMjAwMi0wMy0zNSBoYWQgYW4gYWRkaXRpb25hbCB0d28gZXZlbnRzLgoKQXMgYSBsYXN0IHN0ZXAsIGxldCdzIHJldHVybiB0byBvdXIgZnJlcXVlbmN5IHBsb3RzIHNob3dpbmcgdGhlIG51bWJlciBldmVudHMgcGVyIHNhbXBsZToKCmBgYHtyIGVjaG8gPSBGQUxTRX0KIyBQbG90IGV2ZW50cyBwZXIgU2l0ZUlELURhdGUgc2FtZSBhcyBiZWZvcmUsCmV2ZW50cy4zICU+JSBncm91cF9ieShTaXRlSUQsIERhdGUpICU+JSBzdW1tYXJpc2UoZXZlbnRzID0gbl9kaXN0aW5jdChFdmVudElEKSkgJT4lCiAgZ2dwbG90KGRhdGEgPSAuLCBhZXMoeCA9IGV2ZW50cykpICsKICBnZW9tX2JhcihhZXMoeSA9IGFmdGVyX3N0YXQoY291bnQpKSkgKwogIGdlb21fdGV4dChzdGF0ID0gJ2NvdW50JywgYWVzKGxhYmVsID0gYWZ0ZXJfc3RhdChjb3VudCkpLCB2anVzdCA9IC0wLjUpICsKICBsYWJzKHRpdGxlID0gIkZyZXF1ZW5jeSBvZiBTaXRlSUQtRGF0ZSBwYWlycyBieSAjIG9mIGV2ZW50cyAocmVwbGljYXRlcykiKQojIGdnc2F2ZSgibmZhX2ZyZXEtU2l0ZSZEYXRlLWJ5LXNlaW5lcy5wbmciLCBwbG90ID0gbGFzdF9wbG90KCksIGRldmljZSA9ICdwbmcnLCBwYXRoID0gZmlsZS5wYXRoKGRpci5maWdzKSkKCiMgQW5kIGV2ZW50cyBwZXIgdmlzaXQKdmlzaXRzICU+JSBncm91cF9ieShWaXNpdElEKSAlPiUgc3VtbWFyaXNlKGV2ZW50cyA9IG5fZGlzdGluY3QoRXZlbnRJRCkpICU+JQogIGdncGxvdChkYXRhID0gLiwgYWVzKHggPSBldmVudHMpKSArCiAgZ2VvbV9iYXIoYWVzKHkgPSBhZnRlcl9zdGF0KGNvdW50KSkpICsKICBnZW9tX3RleHQoc3RhdCA9ICdjb3VudCcsIGFlcyhsYWJlbCA9IGFmdGVyX3N0YXQoY291bnQpKSwgdmp1c3QgPSAtMC41KSArCiAgbGFicyh0aXRsZSA9ICJGcmVxdWVuY3kgb2YgdmlzaXRzIGJ5ICMgb2YgZXZlbnRzIChyZXBsaWNhdGVzKSIpCiMgZ2dzYXZlKCJuZmFfZnJlcS12aXNpdHMtYnktc2VpbmVzLnBuZyIsIHBsb3QgPSBsYXN0X3Bsb3QoKSwgZGV2aWNlID0gJ3BuZycsIHBhdGggPSBmaWxlLnBhdGgoZGlyLmZpZ3MpKQpgYGAKCldvdy4gVGhlcmUgYXJlIHdheSBsZXNzIHNpbmdsZS1wdWxsIHNhbXBsZXMhIEFsc28sIHRoZXJlIGFyZSBzb21lIGludGVyZXN0aW5nIHNhbXBsZXMgd2l0aCByZXBsaWNhdGVzIG9mIDksIDEwLCBhbmQgMTIuCgpgYGB7ciBlY2hvID0gRkFMU0V9CiMgQ2xlYW4gdXAgb3VyIGVudmlyb25tZW50IGZvciB0aGUgbmV4dCBzdGVwOgpybShsaXN0ID0gbHMoKVshbHMoKSAlaW4lIGMoJ3Zpc2l0cycsICdjYXRjaC4xJywgJ2RhdGEnKV0pCgojIFJlc2V0IGRpcmVjdG9yeQp3ZCA9IGhlcmUoKQpkaXJzID0gd2QgJT4lIGxpc3QuZmlsZXMoKSAlPiUgc3RyX3N1YnNldChwYXR0ZXJuID0gIl5SRUFETUV8XkxJQ0VOU0V8Lm1kJHwuUnByb2okIiwgbmVnYXRlID0gVFJVRSkKZm9yIChpIGluIHNlcV9hbG9uZyhkaXJzKSkgewogIG5hbWUgPSBzdHJfcmVwbGFjZV9hbGwoZGlyc1tpXSwgIl4iLCAiZGlyLiIpCiAgcGF0aCA9IHN0cl9yZXBsYWNlX2FsbChkaXJzW2ldLCAiXiIsIHN0cl9jKHdkLCAiLyIpKQogIGFzc2lnbihuYW1lLCBwYXRoKQogIHJtKHBhdGgsIGkpCn0KYGBgCgojIFdyYW5nbGUgY2F0Y2ggZGF0YQoKU2ltaWxhciB0byBvdXIgd3JhbmdsZSBvZiBzaXRlIGluZm9ybWF0aW9uLCB3ZSB3YW50IHRvIGhvbmUgdGhlIGJyZWFkdGggb2YgdGhlIGNhdGNoIGRhdGEgd2UgdXNlLiBBdCB0aGlzIHN0YWdlLCB3ZSBkb24ndCB3YW50IHRvIG1ha2UgYW55ICppbnRlcnByZXRhdGlvbnMqIGFib3V0IHRoZSBjYXRjaC4gSW5zdGVhZCwgdGhlIGdvYWwgaGVyZSBpcyB0byB3ZWVkIG91dCBvYmplY3RpdmVseSB1bm5lZWRlZCBhbmQgc3VzcGljaW91cyBjYXNlcyBhbmQgZW5zdXJlIHRoZSBjb25zaXN0ZW5jeSBvZiB0aGUgZGF0YSAoZS5nLiwgYXJlIGFsbCBwb3NzaWJsZSBzcGVjaWVzIG5hbWVkIGFjdHVhbCBuYW1lcz8pLgoKIyMgU2V0IHVwCgpJZiBpdCB3ZXJlIG5lY2Vzc2FyeSB3ZSB3b3VsZCBsb2FkIGFkZGl0aW9uYWwgcGFja2FnZXMsIGRlZmluZSBkaXJlY3Rvcmllcywgc2V0IG9wdGlvbnMsIHNvdXJjZSBzY3JpcHRzLiBCdXQgd2UgZG8gbm90IGhhdmUgYW55IGFkZGl0aW9uYWwgc2V0IHVwIHRvIGRvIGF0IHRoaXMgcG9pbnQuCgpgYGB7ciBlY2hvID0gRkFMU0V9CiMgUGFja2FnZXMKCiMgRGlyZWN0b3J5CgojIE9wdGlvbnMKCiMgU291cmNlCgpgYGAKCiMjIFJlYWQgaW4gZGF0YQoKTm90aGluZyB0byBkbyBoZXJlLgoKIyMgRWRpaW5nIHRheG9ub215CgpgYGB7cn0Kc2tpbShjYXRjaC4xKQpgYGAKCk9uZSBuaWNlIHRoaW5nIGFib3V0ICoqc2tpbSgpKiogaXMgdGhhdCBpdCBxdWlja2x5IHRlbGxzIHlvdSB3aGVyZSB0aGUgaW5mb3JtYXRpb24gaXMgbWlzc2luZyBhbmQgaG93IG11Y2ggaXMgbWlzc2luZy4gV2Ugc2VlIHRoYXQgdGhlcmUgYXJlIG5pbmUgY2FzZXMgb2YgbWlzc2luZyBkYXRhIGF0IHRoZSBmYW1pbHkgbGV2ZWwuIFNvbWV0aGluZyBlbHNlIHRoYXQganVtcHMgb3V0IHRvIG1lIGlzIHRoYXQgdGhlIG51bWJlciBvZiB1bmlxdWUgc3BlY2llcyBjb21tb24gbmFtZXMgaXMgbm90IHRoZSBzYW1lIGFzIHRoZSBudW1iZXIgb2YgdW5pcXVlIHNwZWNpZXMgc2NpZW50aWZpYyBuYW1lcy4KCkknbGwgYWxzbyBjaGFuZ2UgdGhlICdVbm1lYXN1cmVkJyB2YXJpYWJsZSB0byBhIGNvdW50IGFidW5kYW5jZSwgd2hpY2ggbWF5IG5vdCBiZSB0b3RhbGx5IG5lY2Vzc2FyeSBidXQgd2lsbCBtYWtlIGl0IGVhc2llciBmb3IgbWUgdG8gd29yayB3aXRoIHRoZSBkYXRhLgoKQW5vdGhlciB0aGluZyB0byBwb2ludCBvdXQgaXMgdGhhdCBsZXNzIHRoYW4gaGFsZiBvZiB0aGUgZGF0YSBhcHBlYXJzIHRvIGhhdmUgbGlmZSBzdGFnZSBpbmZvcm1hdGlvbi4gRnVydGhlcm1vcmUsIHRoZSBkYXRhIHdpdGggbGVuZ3RoIGluZm9ybWF0aW9uIGlzIGEgbGl0dGxlIG92ZXIgNTAlLCB3aGljaCBpcyBhIHNoYW1lIGJlY2F1c2Ugd2UgY291bGQgaGF2ZSBpbmZlcnJlZCBsaWZlIHN0YWdlIGZyb20gbGVuZ3RoLiBBdCB0aGlzIHBvaW50LCBpdCBkb2Vzbid0IHNlZW0gd29ydGggd2hpbGUgdG8gdG9zcyBvdXQgcG90ZW50aWFsbHkgaGFsZiB0aGUgZGF0YSB0byBpbmNsdWRlIGVpdGhlciBvZiB0aG9zZSB2YXJpYWJsZXMuIEFsdGhvdWdoLCBJIG1heSByZXR1cm4gdG8gdGhpcyBkb3duIHRoZSByb2FkLgoKRmlyc3QsIGxldCdzIGNoZWNrIG91dCBhbGwgdGhlIHRheGEgd2UgaGF2ZSBpbiB0aGUgZGF0YToKCiMjIyBUYXhhIGxpc3QgLSBiZWZvcmUgZWRpdHMgey0gLnRhYnNldCAudGFic2V0LXBpbGxzfQoKIyMjIyBTcGVjaWVzIHstfQoKYGBge3IgZWNobyA9IEZBTFNFfQpjYXRjaC4xICU+JQogIHNlbGVjdChTcF9TY2llbnRpZmljTmFtZSwgU3BfQ29tbW9uTmFtZSkgJT4lCiAgcmVuYW1lKGBTY2llbnRpZmljIE5hbWVgID0gU3BfU2NpZW50aWZpY05hbWUsIGBDb21tb24gTmFtZWAgPSBTcF9Db21tb25OYW1lKSAlPiUKICBkaXN0aW5jdCgpICU+JQogIGFycmFuZ2UoYFNjaWVudGlmaWMgTmFtZWApICU+JQogIERUOjpkYXRhdGFibGUocm93bmFtZXMgPSBGQUxTRSwKICAgICAgICAgICAgICAgIGV4dGVuc2lvbnMgPSAnU2Nyb2xsZXInLAogICAgICAgICAgICAgICAgb3B0aW9ucyA9IGxpc3QoCiAgICAgICAgICAgICAgICAgIGRlZmVyUmVuZGVyID0gVFJVRSwKICAgICAgICAgICAgICAgICAgc2Nyb2xsWSA9IDUwMCwKICAgICAgICAgICAgICAgICAgc2Nyb2xsZXIgPSBUUlVFKSkKYGBgCgojIyMjIEZhbWlseSB7LX0KCmBgYHtyIGVjaG8gPSBGQUxTRX0KY2F0Y2guMSAlPiUKICBzZWxlY3QoRmFtX1NjaWVudGlmaWNOYW1lLCBGYW1fQ29tbW9uTmFtZSkgJT4lCiAgcmVuYW1lKGBTY2llbnRpZmljIE5hbWVgID0gRmFtX1NjaWVudGlmaWNOYW1lLCBgQ29tbW9uIE5hbWVgID0gRmFtX0NvbW1vbk5hbWUpICU+JQogIGRpc3RpbmN0KCkgJT4lCiAgYXJyYW5nZShgU2NpZW50aWZpYyBOYW1lYCkgJT4lCiAgRFQ6OmRhdGF0YWJsZShyb3duYW1lcyA9IEZBTFNFLAogICAgICAgICAgICAgICAgZXh0ZW5zaW9ucyA9ICdTY3JvbGxlcicsCiAgICAgICAgICAgICAgICBvcHRpb25zID0gbGlzdCgKICAgICAgICAgICAgICAgICAgZGVmZXJSZW5kZXIgPSBUUlVFLAogICAgICAgICAgICAgICAgICBzY3JvbGxZID0gNTAwLAogICAgICAgICAgICAgICAgICBzY3JvbGxlciA9IFRSVUUpKQpgYGAKClRoZSBkaXNjcmVwYW5jeSBiZXR3ZWVuIHNwZWNpZXMgY29tbW9uIGFuZCBzY2llbnRpZmljIG5hbWVzIGhhcyB0byBkbyB3aXRoIHRoZSBjYXNlcyB3aGVyZSBmaXNoIHdlcmUgZ2l2ZW4gdW5pZGVudGlmaWVkIG9yIGp1dmVuaWxlIGxhYmVscy4gTm90aGluZyB0byBmaXggaGVyZSBidXQgd2UnbGwganVzdCBtYWtlIHN1cmUgdG8gdXNlIHNjaWVudGlmaWMgbmFtZXMgaW4gYW5hbHlzZXMuCgpJIGFsc28gbm90aWNlIGEgY291cGxlIG9mIGNhc2VzIHdoZXJlIHNwZWNpZXMgYXJlIGNhbGxlZCBzb21ldGhpbmcgJ29yJyBzb21ldGhpbmcgZWxzZToKCjEuICBBbmlzYXJjaHVzIG1lZGl1cyBvciBMdW1wZW51cyBmYWJyaWNpaQoyLiAgT3NtZXJpZGFlIG9yIENsdXBpZGFlIChtaXNzcGVsbGVkKQoKTG9va2luZyBhdCB0aGUgZmFtaWx5IHRhYmxlLCB3ZSBkb24ndCBzZWUgJ09zbWVyaWRhZSBvciBDbHVwZWlkYWUnLCBzbyBJIHRoaW5rIHRoYXQgcmVwbGFjaW5nIHRoZXNlICdvcicgY2FzZXMgd2l0aCB0aGVpciBhc3NvY2lhdGVkIGZhbWlseSBzY2llbnRpZmljIG5hbWUgc2hvdWxkIHN1ZmZpY2UuCgpTcGVha2luZyBvZiB0aGUgZmFtaWx5IHRhYmxlLCBJIHNlZSB0aGF0IHRob3NlIG1pc3NpbmcgZGF0YSBzaG93IHVwIGFzIE5BJ3MuIFdlJ2xsIHRha2UgYSBsb29rIGF0IGl0IGFuZCBtYWtlIGEganVkZ21lbnQgY2FsbCBvbiBob3cgdG8gZGVhbCB3aXRoIHRoZS4KCkxhc3QsIEknbGwgYWxzbyBjcmVhdGUgYSBnZW51cyBjbGFzc2lmaWNhdGlvbiwgd2hpY2ggY291bGQgZW5kIHVwIGJlaW5nIHRoZSBsZXZlbCBhdCB3aGljaCB3ZSBkbyB0aGUgYW5hbHlzZXMsIGRlcGVuZGluZyBvbiB3aGF0IHRoYXQgbG9va3MgbGlrZS4KCkkgZG8gbm90IHNob3cgcmVzdWx0cyBvZiB0aGUgYmVsb3cgY29kZSwgYnV0IEkgcHJvdmlkZWQgYW4gZWRpdGVkIHRheGEgbGlzdCBhbmQgc2VlIGFsc28gY29tbWVudHMgZm9yIHN0ZXBzIHRha2VuLgoKYGBge3IgcmVzdWx0cyA9ICJoaWRlIn0KIyBDaGVjayBvdXQgdGhlIGNhc2VzIHdoZXJlIGZhbWlseSBpcyBtaXNzaW5nCmZpbHRlcihjYXRjaC4xLCBpcy5uYShGYW1fU2NpZW50aWZpY05hbWUpKQojIExvb2sgdXAgdGhlIHZpc2l0IGluZm8gZm9yIHRoZXNlIGNhc2VzCmZpbHRlcih2aXNpdHMsIEV2ZW50SUQgJWluJSBjYXRjaC4xJEV2ZW50SURbd2hpY2goaXMubmEoY2F0Y2guMSRGYW1fU2NpZW50aWZpY05hbWUpKV0pCiMgQ2hlY2sgb3V0IGV2ZXJ5dGhpbmcgZWxzZSBjYXVnaHQgZHVyaW5nIHRoZXNlIHZpc2l0cwpmaWx0ZXIoY2F0Y2guMSwgRXZlbnRJRCAlaW4lIGNhdGNoLjEkRXZlbnRJRFt3aGljaChpcy5uYShjYXRjaC4xJEZhbV9TY2llbnRpZmljTmFtZSkpXSkKIyBTaW5jZSB0aGVzZSBuaW5lIE5BcyBtYWtlIHVwIGEgc21hbGwgcG9ydGlvbiBvZiB0aGVpciBzYW1wbGVzLAojIFRoZSBzaW1wbGVzdCBzb2x2ZSBoZXJlIGlzIHRvIGp1c3QgcmVtb3ZlIHRoZSBkYXRhLgoKIyBDcmVhdGUgYSBuZXcgb2JqZWN0IGFmdGVyIGVkaXRzIG1hZGUgdG8gdGF4b25vbXkKY2F0Y2guMiA9IGNhdGNoLjEgJT4lCiAgIyBEcm9wIHRoZSBuaW5lIGluc3RhbmNlcyBvZiBOQSBjYXRjaAogIGRyb3BfbmEoRmFtX1NjaWVudGlmaWNOYW1lKSAlPiUKICAjIEZpbmQgdGhlICdvcicgc3BlY2llcyBhbmQgcmVwbGFjZSB3aXRoIEZhbV9TY2llbnRpZmljTmFtZSB2YWx1ZQogIG11dGF0ZShTcF9TY2llbnRpZmljTmFtZSA9IGlmZWxzZShzdHJfZGV0ZWN0KFNwX1NjaWVudGlmaWNOYW1lLCBwYXR0ZXJuID0gIiBvciAiKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgRmFtX1NjaWVudGlmaWNOYW1lLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBTcF9TY2llbnRpZmljTmFtZSksCiAgICAgICAgICMgQ3JlYXRlIGdlbnVzIGNsYXNzIGJ5IHRha2luZyBmaXJzdCB3b3JkIG9mIFNwX1NjaWVudGlmaWNOYW1lCiAgICAgICAgIEdlbl9TY2llbnRpZmljTmFtZSA9IHdvcmQoU3BfU2NpZW50aWZpY05hbWUsIDEpKQpgYGAKCiMjIyBUYXhhIGxpc3QgLSBhZnRlciBlZGl0cyB7LSAudGFic2V0IC50YWJzZXQtcGlsbHN9CgojIyMjIFNwZWNpZXMgey19CgpgYGB7ciBlY2hvID0gRkFMU0V9CmNhdGNoLjIgJT4lCiAgc2VsZWN0KFNwX1NjaWVudGlmaWNOYW1lLCBTcF9Db21tb25OYW1lKSAlPiUKICByZW5hbWUoYFNjaWVudGlmaWMgTmFtZWAgPSBTcF9TY2llbnRpZmljTmFtZSwgYENvbW1vbiBOYW1lYCA9IFNwX0NvbW1vbk5hbWUpICU+JQogIGRpc3RpbmN0KCkgJT4lCiAgYXJyYW5nZShgU2NpZW50aWZpYyBOYW1lYCkgJT4lCiAgRFQ6OmRhdGF0YWJsZShyb3duYW1lcyA9IEZBTFNFLAogICAgICAgICAgICAgICAgZXh0ZW5zaW9ucyA9ICdTY3JvbGxlcicsCiAgICAgICAgICAgICAgICBvcHRpb25zID0gbGlzdCgKICAgICAgICAgICAgICAgICAgZGVmZXJSZW5kZXIgPSBUUlVFLAogICAgICAgICAgICAgICAgICBzY3JvbGxZID0gNTAwLAogICAgICAgICAgICAgICAgICBzY3JvbGxlciA9IFRSVUUpKQpgYGAKCiMjIyMgR2VudXMgey19CgpgYGB7ciBlY2hvID0gRkFMU0V9CmNhdGNoLjIgJT4lCiAgc2VsZWN0KGBTY2llbnRpZmljIE5hbWVgID0gR2VuX1NjaWVudGlmaWNOYW1lKSAlPiUKICBkaXN0aW5jdCgpICU+JQogIGFycmFuZ2UoYFNjaWVudGlmaWMgTmFtZWApICU+JQogIERUOjpkYXRhdGFibGUocm93bmFtZXMgPSBGQUxTRSwKICAgICAgICAgICAgICAgIGV4dGVuc2lvbnMgPSAnU2Nyb2xsZXInLAogICAgICAgICAgICAgICAgb3B0aW9ucyA9IGxpc3QoCiAgICAgICAgICAgICAgICAgIGRlZmVyUmVuZGVyID0gVFJVRSwKICAgICAgICAgICAgICAgICAgc2Nyb2xsWSA9IDUwMCwKICAgICAgICAgICAgICAgICAgc2Nyb2xsZXIgPSBUUlVFKSkKYGBgCgojIyMjIEZhbWlseSB7LX0KCmBgYHtyIGVjaG8gPSBGQUxTRX0KY2F0Y2guMiAlPiUKICBzZWxlY3QoRmFtX1NjaWVudGlmaWNOYW1lLCBGYW1fQ29tbW9uTmFtZSkgJT4lCiAgcmVuYW1lKGBTY2llbnRpZmljIE5hbWVgID0gRmFtX1NjaWVudGlmaWNOYW1lLCBgQ29tbW9uIE5hbWVgID0gRmFtX0NvbW1vbk5hbWUpICU+JQogIGRpc3RpbmN0KCkgJT4lCiAgYXJyYW5nZShgU2NpZW50aWZpYyBOYW1lYCkgJT4lCiAgRFQ6OmRhdGF0YWJsZShyb3duYW1lcyA9IEZBTFNFLAogICAgICAgICAgICAgICAgZXh0ZW5zaW9ucyA9ICdTY3JvbGxlcicsCiAgICAgICAgICAgICAgICBvcHRpb25zID0gbGlzdCgKICAgICAgICAgICAgICAgICAgZGVmZXJSZW5kZXIgPSBUUlVFLAogICAgICAgICAgICAgICAgICBzY3JvbGxZID0gNTAwLAogICAgICAgICAgICAgICAgICBzY3JvbGxlciA9IFRSVUUpKQpgYGAKCiMjIEFidW5kYW5jZSBhbmQgb2NjdXJlbmNlCgojIyMgJ1VubWVhc3VyZWQnIGludG8gJ0NvdW50JwoKVGhlIGJlbG93IGNvZGUgY2hhbmdlcyB0aGUgJ1VubWVhc3VyZWQnIHBhcmFtZXRlciBpbnRvICdDb3VudCcgYWJ1bmRhbmNlOgoKYGBge3IgcmVzdWx0cyA9ICJoaWRlIn0KY2F0Y2guMyA9IG11dGF0ZShjYXRjaC4yLCBDb3VudCA9IGlmZWxzZShVbm1lYXN1cmVkID09IDAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgMSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBVbm1lYXN1cmVkKSkgJT4lCiAgc2VsZWN0KC1Vbm1lYXN1cmVkKSAjIHJlbW92ZSB1bnVzZWQgcGFyYW1ldGVyCmBgYAoKTm93IHdlIGNhbiBtYWtlIGdyYXBocyB0byBzZWUgd2hhdCBraW5kIG9mIGNhdGNoIHdlIGhhdmUuIEZvciBlYWNoIHRheGEgbGV2ZWwsIEknbGwgbWFrZSByYXcgYWJ1bmRhbmNlIGFuZCBmcmVxdWVuY3kgb2Ygb2NjdXJyZW5jZSBncmFwaHMuCgojIyMgVmlzdWFsaXppbmcgY2F0Y2ggZGF0YSB7LSAudGFic2V0IC50YWJzZXQtcGlsbHN9CgojIyMjIFNwZWNpZXMgey19CgpgYGB7ciBlY2hvID0gRkFMU0UsIGZpZy53aWR0aCA9IDEyLCBmaWcuaGVpZ2h0ID0gOH0KIyBTcGVjaWVzIGFidW5kYW5jZQpzcC5hYnVuID0gY2F0Y2guMyAlPiUKICBzdW1tYXJpc2UoQWJ1bmRhbmNlID0gc3VtKENvdW50KSwgLmJ5ID0gU3BfU2NpZW50aWZpY05hbWUpICU+JQogIG11dGF0ZShTcF9TY2llbnRpZmljTmFtZSA9IGZjdF9yZW9yZGVyKGFzLmZhY3RvcihTcF9TY2llbnRpZmljTmFtZSksIGRlc2MoQWJ1bmRhbmNlKSkpCiMgUGxvdApnZ3Bsb3QoZGF0YSA9IHNwLmFidW4sIGFlcyh4ID0gU3BfU2NpZW50aWZpY05hbWUsIHkgPSBBYnVuZGFuY2UpKSArCiAgZ2VvbV9jb2woKSArCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IEFidW5kYW5jZSksIHZqdXN0ID0gLTAuNSwgc2l6ZSA9IDIpICsKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBoanVzdCA9IDEpKSArCiAgbGFicyh0aXRsZSA9ICdTcGVjaWVzIGFidW5kYW5jZScsIHggPSAnU3BlY2llcycpCiMgZ2dzYXZlKCJuZmFfc3BfYWJ1bl9yYXcucG5nIiwgcGxvdCA9IGxhc3RfcGxvdCgpLCBkZXZpY2UgPSAncG5nJywgcGF0aCA9IGZpbGUucGF0aChkaXIuZmlncykpCgojIFNwZWNpZXMgZnJlcXVlbmN5IG9mIG9jY3VycmVuY2UKc3AuZnJlcS5vY2N1ciA9IGxlZnRfam9pbihjYXRjaC4zLCBzZWxlY3QodmlzaXRzLCBWaXNpdElELCBFdmVudElEKSwgYnkgPSAiRXZlbnRJRCIpICU+JQogIHN1bW1hcmlzZShQcmVzZW5jZSA9IG5fZGlzdGluY3QoU3BfU2NpZW50aWZpY05hbWUpLCAuYnkgPSBjKFZpc2l0SUQsIFNwX1NjaWVudGlmaWNOYW1lKSkgJT4lCiAgc3VtbWFyaXNlKE9jY3VycmVuY2UgPSBzdW0oUHJlc2VuY2UpLCAuYnkgPSBTcF9TY2llbnRpZmljTmFtZSkgJT4lCiAgbXV0YXRlKFBlcmNfT2NjdXJyZW5jZSA9IHJvdW5kKE9jY3VycmVuY2UgLyBuX2Rpc3RpbmN0KHZpc2l0cyRWaXNpdElEKSAqIDEwMCwgMiksCiAgICAgICAgIFNwX1NjaWVudGlmaWNOYW1lID0gZmN0X3Jlb3JkZXIoYXMuZmFjdG9yKFNwX1NjaWVudGlmaWNOYW1lKSwgZGVzYyhQZXJjX09jY3VycmVuY2UpKSkKIyBQbG90CmdncGxvdChkYXRhID0gc3AuZnJlcS5vY2N1ciwgYWVzKHggPSBTcF9TY2llbnRpZmljTmFtZSwgeSA9IFBlcmNfT2NjdXJyZW5jZSkpICsKICBnZW9tX2NvbCgpICsKICBnZW9tX3RleHQoYWVzKGxhYmVsID0gcm91bmQoUGVyY19PY2N1cnJlbmNlLCAxKSksIHNpemUgPSAyLCB2anVzdCA9IC0wLjUpICsKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBoanVzdCA9IDEpKSArCiAgICBsYWJzKHRpdGxlID0gJ1NwZWNpZXMgZnJlcXVlbmN5IG9mIG9jY3VycmVuY2UnLCB4ID0gJ1NwZWNpZXMnLCB5ID0gJ1BlcmNlbnQgb2NjdXJyZW5jZScpCiMgZ2dzYXZlKCJuZmFfc3BfZnJlcS1vY2N1ci5wbmciLCBwbG90ID0gbGFzdF9wbG90KCksIGRldmljZSA9ICdwbmcnLCBwYXRoID0gZmlsZS5wYXRoKGRpci5maWdzKSkKCiMgVGFibGUgb2Ygc3BlY2llcyBhYnVuZGFuY2UgYW5kIG9jY3VycmVuY2UKZnVsbF9qb2luKHNwLmFidW4sIHNwLmZyZXEub2NjdXIsIGJ5ID0gJ1NwX1NjaWVudGlmaWNOYW1lJykgJT4lCiAgc2VsZWN0KGBTY2llbnRpZmljIE5hbWVgID0gU3BfU2NpZW50aWZpY05hbWUsIGBQZXJjZW50IE9jY3VycmVuY2VgID0gUGVyY19PY2N1cnJlbmNlLCBBYnVuZGFuY2UpICU+JQogIGFycmFuZ2UoYFNjaWVudGlmaWMgTmFtZWApICU+JQogIERUOjpkYXRhdGFibGUocm93bmFtZXMgPSBGQUxTRSwKICAgICAgICAgICAgICAgIGV4dGVuc2lvbnMgPSAnU2Nyb2xsZXInLAogICAgICAgICAgICAgICAgb3B0aW9ucyA9IGxpc3QoCiAgICAgICAgICAgICAgICAgIGRlZmVyUmVuZGVyID0gVFJVRSwKICAgICAgICAgICAgICAgICAgc2Nyb2xsWSA9IDUwMCwKICAgICAgICAgICAgICAgICAgc2Nyb2xsZXIgPSBUUlVFKSkKYGBgCgojIyMjIEdlbnVzIHstfQoKYGBge3IgZWNobyA9IEZBTFNFLCBmaWcud2lkdGggPSAxMiwgZmlnLmhlaWdodCA9IDh9CiMgR2VudXMgYWJ1bmRhbmNlCmdlbi5hYnVuID0gY2F0Y2guMyAlPiUKICBzdW1tYXJpc2UoQWJ1bmRhbmNlID0gc3VtKENvdW50KSwgLmJ5ID0gR2VuX1NjaWVudGlmaWNOYW1lKSAlPiUKICBtdXRhdGUoR2VuX1NjaWVudGlmaWNOYW1lID0gZmN0X3Jlb3JkZXIoYXMuZmFjdG9yKEdlbl9TY2llbnRpZmljTmFtZSksIGRlc2MoQWJ1bmRhbmNlKSkpCiMgUGxvdApnZ3Bsb3QoZGF0YSA9IGdlbi5hYnVuLCBhZXMoeCA9IEdlbl9TY2llbnRpZmljTmFtZSwgeSA9IEFidW5kYW5jZSkpICsKICBnZW9tX2NvbCgpICsKICBnZW9tX3RleHQoYWVzKGxhYmVsID0gQWJ1bmRhbmNlKSwgdmp1c3QgPSAtMC41LCBzaXplID0gMikgKwogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIGhqdXN0ID0gMSkpICsKICBsYWJzKHRpdGxlID0gJ0dlbnVzIGFidW5kYW5jZScsIHggPSAnR2VudXMnKQojIGdnc2F2ZSgibmZhX2dlbl9hYnVuX3Jhdy5wbmciLCBwbG90ID0gbGFzdF9wbG90KCksIGRldmljZSA9ICdwbmcnLCBwYXRoID0gZmlsZS5wYXRoKGRpci5maWdzKSkKCiMgR2VudXMgZnJlcXVlbmN5IG9mIG9jY3VycmVuY2UKZ2VuLmZyZXEub2NjdXIgPSBsZWZ0X2pvaW4oY2F0Y2guMywgc2VsZWN0KHZpc2l0cywgVmlzaXRJRCwgRXZlbnRJRCksIGJ5ID0gIkV2ZW50SUQiKSAlPiUKICBzdW1tYXJpc2UoUHJlc2VuY2UgPSBuX2Rpc3RpbmN0KEdlbl9TY2llbnRpZmljTmFtZSksIC5ieSA9IGMoVmlzaXRJRCwgR2VuX1NjaWVudGlmaWNOYW1lKSkgJT4lCiAgc3VtbWFyaXNlKE9jY3VycmVuY2UgPSBzdW0oUHJlc2VuY2UpLCAuYnkgPSBHZW5fU2NpZW50aWZpY05hbWUpICU+JQogIG11dGF0ZShQZXJjX09jY3VycmVuY2UgPSByb3VuZChPY2N1cnJlbmNlIC8gbl9kaXN0aW5jdCh2aXNpdHMkVmlzaXRJRCkgKiAxMDAsIDIpLAogICAgICAgICBHZW5fU2NpZW50aWZpY05hbWUgPSBmY3RfcmVvcmRlcihhcy5mYWN0b3IoR2VuX1NjaWVudGlmaWNOYW1lKSwgZGVzYyhQZXJjX09jY3VycmVuY2UpKSkKIyBQbG90IApnZ3Bsb3QoZGF0YSA9IGdlbi5mcmVxLm9jY3VyLCBhZXMoeCA9IEdlbl9TY2llbnRpZmljTmFtZSwgeSA9IFBlcmNfT2NjdXJyZW5jZSkpICsKICBnZW9tX2NvbCgpICsKICBnZW9tX3RleHQoYWVzKGxhYmVsID0gcm91bmQoUGVyY19PY2N1cnJlbmNlLCAxKSksIHNpemUgPSAyLCB2anVzdCA9IC0wLjUpICsKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBoanVzdCA9IDEpKSArCiAgICBsYWJzKHRpdGxlID0gJ0dlbnVzIGZyZXF1ZW5jeSBvZiBvY2N1cnJlbmNlJywgeCA9ICdHZW51cycsIHkgPSAnUGVyY2VudCBvY2N1cnJlbmNlJykKIyBnZ3NhdmUoIm5mYV9nZW5fZnJlcS1vY2N1ci5wbmciLCBwbG90ID0gbGFzdF9wbG90KCksIGRldmljZSA9ICdwbmcnLCBwYXRoID0gZmlsZS5wYXRoKGRpci5maWdzKSkKCgojIFRhYmxlIG9mIGdlbnVzIGFidW5kYW5jZSBhbmQgb2NjdXJyZW5jZQpmdWxsX2pvaW4oZ2VuLmFidW4sIGdlbi5mcmVxLm9jY3VyLCBieSA9ICdHZW5fU2NpZW50aWZpY05hbWUnKSAlPiUKICBzZWxlY3QoYFNjaWVudGlmaWMgTmFtZWAgPSBHZW5fU2NpZW50aWZpY05hbWUsIGBQZXJjZW50IE9jY3VycmVuY2VgID0gUGVyY19PY2N1cnJlbmNlLCBBYnVuZGFuY2UpICU+JQogIGFycmFuZ2UoYFNjaWVudGlmaWMgTmFtZWApICU+JQogIERUOjpkYXRhdGFibGUocm93bmFtZXMgPSBGQUxTRSwKICAgICAgICAgICAgICAgIGV4dGVuc2lvbnMgPSAnU2Nyb2xsZXInLAogICAgICAgICAgICAgICAgb3B0aW9ucyA9IGxpc3QoCiAgICAgICAgICAgICAgICAgIGRlZmVyUmVuZGVyID0gVFJVRSwKICAgICAgICAgICAgICAgICAgc2Nyb2xsWSA9IDUwMCwKICAgICAgICAgICAgICAgICAgc2Nyb2xsZXIgPSBUUlVFKSkKYGBgCgojIyMjIEZhbWlseSB7LX0KCmBgYHtyIGVjaG8gPSBGQUxTRSwgZmlnLndpZHRoID0gMTIsIGZpZy5oZWlnaHQgPSA4fQojIEZhbWlseSBhYnVuZGFuY2UKZmFtLmFidW4gPSBjYXRjaC4zICU+JQogIHN1bW1hcmlzZShBYnVuZGFuY2UgPSBzdW0oQ291bnQpLCAuYnkgPSBGYW1fU2NpZW50aWZpY05hbWUpICU+JQogIG11dGF0ZShGYW1fU2NpZW50aWZpY05hbWUgPSBmY3RfcmVvcmRlcihhcy5mYWN0b3IoRmFtX1NjaWVudGlmaWNOYW1lKSwgZGVzYyhBYnVuZGFuY2UpKSkKIyBQbG90CmdncGxvdChkYXQgPSBmYW0uYWJ1biwgYWVzKHggPSBGYW1fU2NpZW50aWZpY05hbWUsIHkgPSBBYnVuZGFuY2UpKSArCiAgZ2VvbV9jb2woKSArCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IEFidW5kYW5jZSksIHZqdXN0ID0gLTAuNSwgc2l6ZSA9IDIpICsKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBoanVzdCA9IDEpKSArCiAgbGFicyh0aXRsZSA9ICdGYW1pbHkgYWJ1bmRhbmNlJywgeCA9ICdGYW1pbHknKQojIGdnc2F2ZSgibmZhX2ZhbV9hYnVuX3Jhdy5wbmciLCBwbG90ID0gbGFzdF9wbG90KCksIGRldmljZSA9ICdwbmcnLCBwYXRoID0gZmlsZS5wYXRoKGRpci5maWdzKSkKCiMgRmFtaWx5IGZyZXF1ZW5jeSBvZiBvY2N1cnJlbmNlCmZhbS5mcmVxLm9jY3VyID0gbGVmdF9qb2luKGNhdGNoLjMsIHNlbGVjdCh2aXNpdHMsIFZpc2l0SUQsIEV2ZW50SUQpLCBieSA9ICJFdmVudElEIikgJT4lCiAgc3VtbWFyaXNlKFByZXNlbmNlID0gbl9kaXN0aW5jdChGYW1fU2NpZW50aWZpY05hbWUpLCAuYnkgPSBjKFZpc2l0SUQsIEZhbV9TY2llbnRpZmljTmFtZSkpICU+JQogIHN1bW1hcmlzZShPY2N1cnJlbmNlID0gc3VtKFByZXNlbmNlKSwgLmJ5ID0gRmFtX1NjaWVudGlmaWNOYW1lKSAlPiUKICBtdXRhdGUoUGVyY19PY2N1cnJlbmNlID0gcm91bmQoT2NjdXJyZW5jZSAvIG5fZGlzdGluY3QodmlzaXRzJFZpc2l0SUQpICogMTAwLCAyKSwKICAgICAgICAgRmFtX1NjaWVudGlmaWNOYW1lID0gZmN0X3Jlb3JkZXIoYXMuZmFjdG9yKEZhbV9TY2llbnRpZmljTmFtZSksIGRlc2MoUGVyY19PY2N1cnJlbmNlKSkpCiMgUGxvdApnZ3Bsb3QoZGF0YSA9IGZhbS5mcmVxLm9jY3VyLCBhZXMoeCA9IEZhbV9TY2llbnRpZmljTmFtZSwgeSA9IFBlcmNfT2NjdXJyZW5jZSkpICsKICBnZW9tX2NvbCgpICsKICBnZW9tX3RleHQoYWVzKGxhYmVsID0gcm91bmQoUGVyY19PY2N1cnJlbmNlLCAxKSksIHNpemUgPSAyLCB2anVzdCA9IC0wLjUpICsKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBoanVzdCA9IDEpKSArCiAgICBsYWJzKHRpdGxlID0gJ0ZhbWlseSBmcmVxdWVuY3kgb2Ygb2NjdXJyZW5jZScsIHggPSAnRmFtaWx5JywgeSA9ICdQZXJjZW50IG9jY3VycmVuY2UnKQojIGdnc2F2ZSgibmZhX2ZhbV9mcmVxLW9jY3VyLnBuZyIsIHBsb3QgPSBsYXN0X3Bsb3QoKSwgZGV2aWNlID0gJ3BuZycsIHBhdGggPSBmaWxlLnBhdGgoZGlyLmZpZ3MpKQoKIyBUYWJsZSBvZiBmYW1pbHkgYWJ1bmRhbmNlIGFuZCBvY2N1cnJlbmNlCmZ1bGxfam9pbihmYW0uYWJ1biwgZmFtLmZyZXEub2NjdXIsIGJ5ID0gJ0ZhbV9TY2llbnRpZmljTmFtZScpICU+JQogIHNlbGVjdChgU2NpZW50aWZpYyBOYW1lYCA9IEZhbV9TY2llbnRpZmljTmFtZSwgYFBlcmNlbnQgT2NjdXJyZW5jZWAgPSBQZXJjX09jY3VycmVuY2UsIEFidW5kYW5jZSkgJT4lCiAgYXJyYW5nZShgU2NpZW50aWZpYyBOYW1lYCkgJT4lCiAgRFQ6OmRhdGF0YWJsZShyb3duYW1lcyA9IEZBTFNFLAogICAgICAgICAgICAgICAgZXh0ZW5zaW9ucyA9ICdTY3JvbGxlcicsCiAgICAgICAgICAgICAgICBvcHRpb25zID0gbGlzdCgKICAgICAgICAgICAgICAgICAgZGVmZXJSZW5kZXIgPSBUUlVFLAogICAgICAgICAgICAgICAgICBzY3JvbGxZID0gNTAwLAogICAgICAgICAgICAgICAgICBzY3JvbGxlciA9IFRSVUUpKQpgYGAKCmBgYHtyfQojIFZpZXcgdGhlIGNvbW1vbiBuYW1lcyBmb3IgcmFyZSBHZW51c2VzLAojIGkuZS4sIG9jY3VyaW5nIGluIDwwLjI1JSBvZiB2aXNpdHMgKDYgb3IgZmV3ZXIgdmlzaXRzKQojIFRoZXNlIGluY2x1ZGUgMzQgZGlmZmVyZW50IHRheGEKZ2VuLmZyZXEub2NjdXIgJT4lCiAgZmlsdGVyKFBlcmNfT2NjdXJyZW5jZSA8IDAuMjUpICU+JQogIG11dGF0ZShHZW5fU2NpZW50aWZpY05hbWUgPSBhcy5jaGFyYWN0ZXIoR2VuX1NjaWVudGlmaWNOYW1lKSkgJT4lCiAgc2VsZWN0KEdlbl9TY2llbnRpZmljTmFtZSkgJT4lCiAgYXJyYW5nZSgpICU+JQogIGtuaXRyOjprYWJsZSgpCgojIFRoZXJlIGFyZSAxMCBmYW1pbGllcyB0aGF0IG9jY3VyIGluIGxlc3MgdGhhbiAxJSBvZiB2aXNpdHMuCiMgV2Ugd2lsbCBjb25zaWRlciB0aGVzZSBvdXIgZXh0cmVtZSByYXJlIGNhc2VzIGFuZCByZW1vdmUgdGhlbSBmcm9tIHRoZSBkZiwKZmFtLmZyZXEub2NjdXIgJT4lCiAgZmlsdGVyKFBlcmNfT2NjdXJyZW5jZSA8IDEpICU+JQogIG11dGF0ZShGYW1fU2NpZW50aWZpY05hbWUgPSBhcy5jaGFyYWN0ZXIoRmFtX1NjaWVudGlmaWNOYW1lKSkgJT4lCiAgc2VsZWN0KEZhbV9TY2llbnRpZmljTmFtZSkgJT4lCiAgYXJyYW5nZSgpICU+JQogIGtuaXRyOjprYWJsZSgpCmBgYAoKIyAgey51bmxpc3RlZCAudW5udW1iZXJlZH0K